﻿ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using DefCore.Interfaces;

namespace DefCore.AI.Pathing
{
    public enum EntityPathRepeatMode {
        IterateOnce = 0,
        IterateForever = 1,
        AutoReverse = 2,
    }

    public class WaypointPath : List<Waypoint>
    {         
        public Waypoint FirstNode { get { return this.First(); } }
        public Waypoint LastNode { get { return this.Last(); } }

        public Waypoint CurrentNode { get { return this[CurrentNodeIndex]; } }
        public Waypoint? NextNode
        { 
            get
            {
                try
                {
                    return this[CurrentNodeIndex + 1];
                }
                catch (Exception)
                {
                    return null;
                }
            }
        }

        public IPathable Actor { get; private set; }
        public EntityPathRepeatMode RepeatMode { get; set; }
        private double _TargetRotation;

        private int _CurrentNodeIndex = 0;
        protected int CurrentNodeIndex
        { 
            get { return _CurrentNodeIndex; }
            set { _CurrentNodeIndex = value; }
        }
        
        public event EventHandler NodeLanding;
        public event EventHandler PathFinished;

        //cTor
        public WaypointPath(IPathable actor)
            : base()
        {
            Actor = actor;
            RepeatMode = EntityPathRepeatMode.IterateOnce;
            this.PathFinished += WaypointPathFinished;
        }        

        public WaypointPath(IPathable actor, params Waypoint[] waypoints)
            : base(waypoints)
        {
            Actor = actor;
            RepeatMode = EntityPathRepeatMode.IterateOnce;
            this.PathFinished += WaypointPathFinished;
        }

        public WaypointPath(IPathable actor, IEnumerable<Waypoint> waypoints)
            : base(waypoints)
        {
            Actor = actor;
            RepeatMode = EntityPathRepeatMode.IterateOnce;
            this.PathFinished += WaypointPathFinished;
        }

        //Methods
        private void StepToNextNode()
        {
            _CurrentNodeIndex++;
        }
        private void WaypointPathFinished(object sender, EventArgs e)
        {
            switch (RepeatMode)
            {
                case EntityPathRepeatMode.IterateOnce: Stop(); break;
                case EntityPathRepeatMode.IterateForever:
                    {
                        Stop();
                        CurrentNodeIndex = 0;
                        Start();
                        break;
                    }
                case EntityPathRepeatMode.AutoReverse:
                    {
                        Stop();
                        this.Reverse();
                        CurrentNodeIndex = 0;
                        Start();
                        break;
                    }
            }
        }

        public virtual void Start()
        {
            Actor.IsControlled = true;    
            //If the actor is not at the start position, move him (no path)
            //if (!Actor.Position.Equals(FirstNode))
            //    Actor.Position.WorldPosition = FirstNode.WaypointPosition;
        }
        public virtual void Stop()
        {
            Actor.IsControlled = false;      
        }
        
        public virtual void UpdatePath(GameTime time)
        {
            if (!Actor.IsControlled)
                return;

            //The path has finished
            if (NextNode == null) {
                Actor.IsControlled = false;
                PathFinished(this, null);
                return;
            }

            //Get the Direction to the next node & Update Target Rotation
            Vector2 DirectionToNextNode = Waypoint.GetDistanceBetween(Actor.Position.WorldPosition, (Waypoint)NextNode);
            _TargetRotation = CurrentNode.AngleTo((Waypoint)NextNode);

            //If there is no distance
            if (Actor.Rotation != _TargetRotation) 
            {
                /* TODO: Fix this for rotation both ways. */
                //Rotate
                double rotDiff = Math.Round(_TargetRotation - Actor.Rotation, 2);
                Actor.Rotation += (float)rotDiff / 5f;

                if (Math.Abs(rotDiff) <= MathHelper.ToRadians(1))
                    Actor.Rotation = CurrentNode.AngleTo((Waypoint)NextNode);                          
                
            }
            else if (DirectionToNextNode.Equals(Vector2.Zero))
            {
                //we landed
                if (NodeLanding != null)
                    NodeLanding(this, null);

                StepToNextNode();
            }
            else
            {
                Vector2 moveDir = Vector2.Zero;

                //Clamp movement to speed and move;
                Vector2 minMoveDistance = DirectionToNextNode;
                minMoveDistance.Normalize();
                minMoveDistance *= Actor.ActorSpeed;

                if (minMoveDistance.Length() <= DirectionToNextNode.Length())
                    moveDir = minMoveDistance;
                else
                    moveDir = DirectionToNextNode;

                //Rotate towards                
                

                //CLAMPING
                moveDir.X = (float)Math.Round(moveDir.X, 2);
                moveDir.Y = (float)Math.Round(moveDir.Y, 2);

                Vector2 newPosition = Actor.Position.WorldPosition + moveDir;
                newPosition.X = (float)Math.Round(newPosition.X, 2);
                newPosition.Y = (float)Math.Round(newPosition.Y, 2);

                Actor.Position.WorldPosition = newPosition;
            }             
        }        
    }
}